using System;
using System.Collections;

using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.X509;

namespace Org.BouncyCastle.Asn1.Pkcs
{
	/**
	 * a Pkcs#7 signer info object.
	 */
	public class SignerInfo
		: Asn1Encodable
	{
		private DerInteger              version;
		private IssuerAndSerialNumber   issuerAndSerialNumber;
		private AlgorithmIdentifier     digAlgorithm;
		private Asn1Set                 authenticatedAttributes;
		private AlgorithmIdentifier     digEncryptionAlgorithm;
		private Asn1OctetString         encryptedDigest;
		private Asn1Set                 unauthenticatedAttributes;

		public static SignerInfo GetInstance(
			object obj)
		{
			if (obj is SignerInfo)
			{
				return (SignerInfo) obj;
			}

			if (obj is Asn1Sequence)
			{
				return new SignerInfo((Asn1Sequence) obj);
			}

			throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj");
		}

		public SignerInfo(
			DerInteger              version,
			IssuerAndSerialNumber   issuerAndSerialNumber,
			AlgorithmIdentifier     digAlgorithm,
			Asn1Set                 authenticatedAttributes,
			AlgorithmIdentifier     digEncryptionAlgorithm,
			Asn1OctetString         encryptedDigest,
			Asn1Set                 unauthenticatedAttributes)
		{
			this.version = version;
			this.issuerAndSerialNumber = issuerAndSerialNumber;
			this.digAlgorithm = digAlgorithm;
			this.authenticatedAttributes = authenticatedAttributes;
			this.digEncryptionAlgorithm = digEncryptionAlgorithm;
			this.encryptedDigest = encryptedDigest;
			this.unauthenticatedAttributes = unauthenticatedAttributes;
		}

		public SignerInfo(
			Asn1Sequence seq)
		{
			IEnumerator e = seq.GetEnumerator();

			e.MoveNext();
			version = (DerInteger) e.Current;

			e.MoveNext();
			issuerAndSerialNumber = IssuerAndSerialNumber.GetInstance(e.Current);

			e.MoveNext();
			digAlgorithm = AlgorithmIdentifier.GetInstance(e.Current);

			e.MoveNext();
			object obj = e.Current;

			if (obj is Asn1TaggedObject)
			{
				authenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) obj, false);

				e.MoveNext();
				digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(e.Current);
			}
			else
			{
				authenticatedAttributes = null;
				digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(obj);
			}

			e.MoveNext();
			encryptedDigest = DerOctetString.GetInstance(e.Current);

			if (e.MoveNext())
			{
				unauthenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current, false);
			}
			else
			{
				unauthenticatedAttributes = null;
			}
		}

		public DerInteger Version { get { return version; } }

		public IssuerAndSerialNumber IssuerAndSerialNumber { get { return issuerAndSerialNumber; } }

		public Asn1Set AuthenticatedAttributes { get { return authenticatedAttributes; } }

		public AlgorithmIdentifier DigestAlgorithm { get { return digAlgorithm; } }

		public Asn1OctetString EncryptedDigest { get { return encryptedDigest; } }

		public AlgorithmIdentifier DigestEncryptionAlgorithm { get { return digEncryptionAlgorithm; } }

		public Asn1Set UnauthenticatedAttributes { get { return unauthenticatedAttributes; } }

		/**
		 * Produce an object suitable for an Asn1OutputStream.
		 * <pre>
		 *  SignerInfo ::= Sequence {
		 *      version Version,
		 *      issuerAndSerialNumber IssuerAndSerialNumber,
		 *      digestAlgorithm DigestAlgorithmIdentifier,
		 *      authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
		 *      digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
		 *      encryptedDigest EncryptedDigest,
		 *      unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
		 *  }
		 *
		 *  EncryptedDigest ::= OCTET STRING
		 *
		 *  DigestAlgorithmIdentifier ::= AlgorithmIdentifier
		 *
		 *  DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
		 * </pre>
		 */
		public override Asn1Object ToAsn1Object()
		{
			Asn1EncodableVector v = new Asn1EncodableVector(
				version, issuerAndSerialNumber, digAlgorithm);

			if (authenticatedAttributes != null)
			{
				v.Add(new DerTaggedObject(false, 0, authenticatedAttributes));
			}

			v.Add(digEncryptionAlgorithm, encryptedDigest);

			if (unauthenticatedAttributes != null)
			{
				v.Add(new DerTaggedObject(false, 1, unauthenticatedAttributes));
			}

			return new DerSequence(v);
		}
	}
}
